﻿#include "MyVideo.h"
#include <QVector3D>
#include <QMatrix4x4>
#include <QOpenGLTexture>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLFramebufferObjectFormat>

// 数据上下文
struct Context {
    QImage image;
};

// 顶点矩阵 Vertex matrix
static const GLfloat vertexVertices[] = {
    -1.0f, -1.0f,
    1.0f, -1.0f,
    -1.0f, 1.0f,
    1.0f, 1.0f,
};

// 纹理矩阵 Texture matrix
static const GLfloat textureVertices[] = {
    0.0f,  1.0f,
    1.0f,  1.0f,
    0.0f,  0.0f,
    1.0f,  0.0f,
};

// OpenGL 渲染
class MyVideoRenderer : public QQuickFramebufferObject::Renderer , protected QOpenGLFunctions
{
public:
    MyVideoRenderer():texture(QOpenGLTexture::Target2D){
        // 初始化 OpenGL 上下文
        initialize();
        texture.create();
    }
    ~MyVideoRenderer(){
        texture.destroy();
    }
    void initialize(){
        initializeOpenGLFunctions();
        glClearColor(1.0f,1.0f,1.0f,1.0f);
        if(!shaderProgram.addCacheableShaderFromSourceFile(QOpenGLShader::Vertex,":/vertexshader.vert")){
            qWarning() << QString("[ERROR] Vertex shader: %1").arg(shaderProgram.log());
        }
        if(!shaderProgram.addCacheableShaderFromSourceFile(QOpenGLShader::Fragment,":/fragmentshader.frag")){
            qWarning() << QString("[ERROR] Vertex shader: %1").arg(shaderProgram.log());
        }

        shaderProgram.bindAttributeLocation("qt_Vertex",indexVvertex);
        shaderProgram.bindAttributeLocation("qt_MultiTexCoord0",indexTexture);

        if(!shaderProgram.link()){
            qWarning() << QString("[ERROR] ShaderProgram link: %1").arg(shaderProgram.log());
        }

        sampler2D = shaderProgram.uniformLocation("qt_Texture0");
        projectionMatrix = shaderProgram.uniformLocation("qt_ModelViewProjectionMatrix");
    }
    virtual QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) override {
        QOpenGLFramebufferObjectFormat format;
        format.setSamples(4);
        return new QOpenGLFramebufferObject(size,format);
    }

    // 渲染
    virtual void render() override {
        glClear(GL_DEPTH_BUFFER_BIT);

        constexpr const float angle = 0.0f;
        constexpr const float scale = 1.0f;
        QMatrix4x4 modelview;
        modelview.rotate(angle, 0.0f, 1.0f, 0.0f);//y
        modelview.rotate(angle, 1.0f, 0.0f, 0.0f);//x
        modelview.rotate(angle, 0.0f, 0.0f, 1.0f);//z
        modelview.scale(scale);
        modelview.translate(0.0f, 0.0f, 0.0f);

        if(!shaderProgram.bind()){
            qWarning() << QString("[ERROR] ShaderProgram bind: %1").arg(shaderProgram.log());
        }

        shaderProgram.setUniformValue(sampler2D,0);
        shaderProgram.setUniformValue(projectionMatrix,modelview);

        shaderProgram.setAttributeArray(indexVvertex,vertexVertices,2,0);
        shaderProgram.setAttributeArray(indexTexture,textureVertices,2,0);

        shaderProgram.enableAttributeArray(indexVvertex);
        shaderProgram.enableAttributeArray(indexTexture);

        texture.bind();

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        shaderProgram.disableAttributeArray(indexVvertex);
        shaderProgram.disableAttributeArray(indexTexture);

        shaderProgram.release();
    }

    // 同步数据
    void synchronize(QQuickFramebufferObject *item) override{
        MyVideo *obj = static_cast<MyVideo *>(item);
        if(obj){
            auto img = obj->context()->image;
            if( img.width() != texture.width() || img.height() != texture.height()){
                // 大小已改变 更新纹理
                textureResize(img.width(),img.height());
                texture.setData(QOpenGLTexture::RGBA,QOpenGLTexture::UInt8,img.constBits());
            }else{
                texture.setData(QOpenGLTexture::RGBA,QOpenGLTexture::UInt8,img.constBits());
            }
        }
    }
    // 重置纹理
    inline void textureResize(int width,int height){
        texture.destroy();
        texture.create();
        texture.setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
        texture.setSize(width,height);
        texture.setFormat(QOpenGLTexture::RGBA8_UNorm);
        texture.allocateStorage();
    }
    //
    QOpenGLShaderProgram shaderProgram;
    GLint sampler2D = -1;
    GLint projectionMatrix = -1;
    QOpenGLTexture texture;
    //顶点&纹理索引
    GLuint indexVvertex = 0;
    GLuint indexTexture = 1;
};

MyVideo::MyVideo(QQuickItem *parent) : QQuickFramebufferObject(parent),m_ctx(new Context)
{
    setMirrorVertically(true);
}

QQuickFramebufferObject::Renderer *MyVideo::createRenderer() const
{
    return new MyVideoRenderer();
}

QSharedPointer<Context> MyVideo::context()
{
    return m_ctx;
}

void MyVideo::newData(const QString &filename)
{
    auto img = QImage(filename);
    if(!img.isNull()){
        newData(img);
    }
}

void MyVideo::newData(const QImage &image)
{
    m_ctx->image = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
    update();
}
